package com.xuecheng.manage_media.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.domain.media.response.MediaCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_media.config.RabbitMQConfig;
import com.xuecheng.manage_media.dao.MediaFileRepository;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

@Service
public class MediaUploadService {
    private static final Logger LOGGER = LoggerFactory.getLogger(MediaUploadService.class);
    @Value("${xc-service-manage-media.upload-location}")
    String  uploadPath;
    @Autowired
    MediaFileRepository mediaFileRepository;
    @Value("${xc-service-manage-media.mq.routingkey‐media‐video}")
    String routingkey_media_video;
    @Autowired
    RabbitTemplate rabbitTemplate;

    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //检查文件是否上传
        //得到文件目录路径
        String fileFolderPath=this.getFileFolderPath(fileMd5);
        //1.得到文件的路径
        String filePath=getFilePath(fileMd5,fileExt);
        File file=new File(filePath);
        //2.查询数据库文件是否存在
        Optional<MediaFile> optional=mediaFileRepository.findById(fileMd5);
        //文件存在则直接返回
        if(file.exists()&&optional.isPresent()){
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }
       //若文件不存在，则检查文件目录是否存在，若不存在则创建
        File fileFolder=new File(fileFolderPath);
        if(!fileFolder.exists()){
            fileFolder.mkdirs();
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /*** 根据文件md5得到文件路径
    * 规则：
     * 一级目录：md5的第一个字符
    * 二级目录：md5的第二个字符
    * 三级目录：md5
    * 文件名：md5+文件扩展名
    * @param fileMd5 文件md5值
    * @param fileExt 文件扩展名
    * @return 文件路径
    */
    private String getFilePath(String fileMd5,String fileExt) {
        String filePath=uploadPath+fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+
                fileMd5+"/"+fileMd5+"."+fileExt;
        return filePath;
    }

    //得到文件目录相对路径，路径中去掉根目录
    private String getFileFolderRelativePath(String fileMd5,String fileExt){
        String filePath = fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" +
                fileMd5 + "/";
        return filePath;
    }

    //得到文件所在目录
    private String getFileFolderPath(String fileMd5){
        String fileFolderPath = uploadPath+ fileMd5.substring(0, 1) + "/" + fileMd5.substring(1,
                2) + "/" + fileMd5 + "/" ;
        return fileFolderPath;
    }

    //检查分块
    public CheckChunkResult checkChunk(String fileMd5, Integer chunk, Integer chunkSize) {
        //获得块文件目录
        String chunkfileFolderPath = getChunkFileFolderPath(fileMd5);
        //块文件的文件名以1 2 3命名，没有扩展名
        File chunkFile=new File(chunkfileFolderPath+chunk);
        if(chunkFile.exists()){
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,true);
        }else {
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,false);
        }
    }

    //得到分块文件目录
    private String getChunkFileFolderPath(String fileMd5) {
        String fileChunkFolderPath = getFileFolderPath(fileMd5) +"/" + "chunks" + "/";
        return fileChunkFolderPath;
    }

    //上传分块文件
    public ResponseResult uploadChunk(MultipartFile file, Integer chunk, String fileMd5) {
        if(file==null){
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_ISNULL);
        }
        //得到分块文件目录
        String chunkFileFolderPath=this.getChunkFileFolderPath(fileMd5);
        //分块文件路径
        String chunkFilePath=chunkFileFolderPath+chunk;

        File chunkFileFolder=new File(chunkFileFolderPath);
        //如果不存在则创建
        if(!chunkFileFolder.exists()){
            chunkFileFolder.mkdirs();
        }
        //上传块文件
        InputStream inputStream=null;
        FileOutputStream outputStream=null;
        try {
          inputStream=  file.getInputStream();
          outputStream=new FileOutputStream(chunkFilePath);
          IOUtils.copy(inputStream,outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }


    public ResponseResult mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //拿到所有文件并合并文件
        //获得文件目录
        String fileChunkFoldFile =this.getChunkFileFolderPath(fileMd5);
        File chunkFile=new File(fileChunkFoldFile);
        File[] files=chunkFile.listFiles();
        List<File> fileList=Arrays.asList(files);
        //创建一个文件写入进行合并
        String filePath=getFilePath(fileMd5,fileExt);
        File mergeFile=new File(filePath);
       mergeFile= this.mergeFile(fileList,mergeFile);
       if(mergeFile==null){
           ExceptionCast.cast(MediaCode.MERGE_FILE_FAIL);
       }
        //校验合并文件的md5是否与前端所传md5是否一致
     boolean checkFileMd5= this.checkFileMd5(mergeFile,fileMd5);
       if(!checkFileMd5){
           ExceptionCast.cast(MediaCode.MERGE_FILE_FAIL);
       }
        //将文件信息写入mogonDB
        //将文件信息保存到数据库
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        mediaFile.setFileName(fileMd5+"."+fileExt);
        mediaFile.setFileOriginalName(fileName);
        //文件路径保存相对路径
        mediaFile.setFilePath(getFileFolderRelativePath(fileMd5,fileExt));
        mediaFile.setFileSize(fileSize);
        mediaFile.setUploadTime(new Date());
        mediaFile.setMimeType(mimetype);
        mediaFile.setFileType(fileExt);
        //状态为上传成功
        mediaFile.setFileStatus("301002");
        MediaFile save = mediaFileRepository.save(mediaFile);
        //向rabbitMq发送消息
        this.sendProcessVideoMsg(mediaFile.getFileId());
        return new ResponseResult(CommonCode.SUCCESS);
    }

    public ResponseResult sendProcessVideoMsg(String mediaId){
        Optional<MediaFile> optional=mediaFileRepository.findById(mediaId);
        if(!optional.isPresent()){
            return new ResponseResult(CommonCode.SUCCESS);
        }
        //发送视频处理消息
        Map<String,String> msgMap=new HashMap<>();
        msgMap.put("mediaId",mediaId);
        String msg=  JSON.toJSONString(msgMap);
        try {
            rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK,routingkey_media_video,msg);
            LOGGER.info("send media process task msg:{}",msg);
        }catch (Exception e){
            e.printStackTrace();
            LOGGER.info("send media process task error,msg is:{},error:{}",msg,e.getMessage());
            return new ResponseResult(CommonCode.FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }
    //合并文件方法
    private File mergeFile(List<File> chunkFileList,File mergeFile){
        try {
        if(mergeFile.exists()){
            mergeFile.delete();
        }else {
                mergeFile.createNewFile();
        }
        //对块文件进行排序
            Collections.sort(chunkFileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    if(Integer.parseInt(o1.getName())>Integer.parseInt(o2.getName())){
                        return 1;
                    }
                    return -1;
                }
            });
          //创建一个写对象
            RandomAccessFile raf_write=new RandomAccessFile(mergeFile,"rw");
            for (File chunkFile:chunkFileList){
                RandomAccessFile raf_read=new RandomAccessFile(chunkFile,"r");
                byte[] b=new byte[1024];
                int len=-1;
                while ((len=raf_read.read(b))!=-1){
                    raf_write.write(b,0,len);
                }
                raf_read.close();
            }
            raf_write.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return mergeFile;
    }
    //md5校验
    private boolean checkFileMd5(File mergeFile,String fieldMd5){
        try {
            FileInputStream inputStream=new FileInputStream(mergeFile);
            //得到文件的md5
          String mergeMd5= DigestUtils.md5Hex(inputStream);
          if(mergeMd5.equalsIgnoreCase(fieldMd5)){
              return true;
          }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}
